import { resourceList } from '@/room/resource/constants'
import { FactoryMemory, FactoryRoomMap, FactoryTarget } from './types'
import { isMyRoom, isRoomInterval } from '@/ayaka'
import { OneToOneTransportData } from '@/room/task/base/transport/types'
import TransportTaskManager from '@/room/task/base/transport'
import { zipMap } from '@/utils/constants'
import ResourceManager from '@/room/resource'
import PCTaskManager from '@/room/task/pc'

/**
 * 工厂控制器
 */
export default class FactoryController {
    /**
     * 工厂搬运底物任务名字
     */
    public static FACTORY_TRANSPORT_SUBSTRATE_TASK_NAME = '工厂搬运底物'

    /**
     * 工厂清理任务名字
     */
    public static FACTORY_CLEAR_PRODUCT_TASK_NAME = '工厂清理产物'

    /**
     * 每个房间 bar 的最小储量
     */
    public static BAR_MIN_AMOUNT = 10000

    /**
     * 不同等级工厂对应的房间
     */
    public static factoryRoomMap: FactoryRoomMap = {}

    /**
     * 运行
     */
    public static run(room: MyRoom) {
        const memory = FactoryController.getMemory(room)
        if (memory.cooldown) {
            memory.cooldown--
            return
        }

        const factory = room.factory
        if (!room.storage || !factory) {
            memory.cooldown = 300
            return
        }

        if (memory.target) {
            const result = factory.produce(memory.target)
            // 底物不足
            if (result === OK || result === ERR_TIRED) {
                memory.cooldown = COMMODITIES[memory.target].cooldown
            } else if (result === ERR_NOT_ENOUGH_RESOURCES) {
                // 此时没有搬运任务就说明搬运完了
                if (
                    !TransportTaskManager.searchTransportTaskByName(
                        room,
                        FactoryController.FACTORY_TRANSPORT_SUBSTRATE_TASK_NAME,
                    ).length
                ) {
                    delete memory.target
                }
                memory.cooldown = 20
            } else if (result === ERR_BUSY && factory.effects.length === 0) {
                // 等级过期了，请求 pc 施法
                if (!PCTaskManager.hasPc(room, PWR_OPERATE_FACTORY)) {
                    memory.cooldown = 300
                    return
                }

                if (PCTaskManager.getTaskByName(room, `${PWR_OPERATE_FACTORY}`)) {
                    memory.cooldown = 10
                    return
                }

                PCTaskManager.addTask(room, `${PWR_OPERATE_FACTORY}`, PWR_OPERATE_FACTORY, factory.id)
                memory.cooldown = 20
            } else {
                if (result === ERR_NOT_OWNER) {
                    // 不是自己的工厂
                    memory.cooldown = 1000
                }
                delete memory.target
            }
        } else {
            if (isRoomInterval(room, 51)) {
                FactoryController.plan(room, factory, memory)
                FactoryController.clear(room, factory, memory)
            }
        }
    }

    /**
     * 获取 memory
     */
    private static getMemory(room: MyRoom) {
        if (!room.memory.factory) room.memory.factory = {}
        return room.memory.factory
    }

    /**
     * 合成规划
     */
    public static plan(room: MyRoom, factory: StructureFactory, memory: FactoryMemory) {
        if (factory.store.getFreeCapacity() < 10000) return

        // 优先选择该等级才能合成的商品
        const storage = room.storage!
        const minEnergy = room.enemys?.length ? 40000 : 10000
        if (storage.store['energy'] < minEnergy) return

        if (factory.level && room.controller.level === 8) {
            FactoryController.addRoom(factory)

            // 该等级高级商品
            const advancedGoods: CommodityConstant[] = []
            for (const goodType of [
                'commodityMetal',
                'commodityBiomass',
                'commoditySilicon',
                'commodityMist',
            ] as const) {
                advancedGoods.push(resourceList[goodType].slice(1)[factory.level] as CommodityConstant)
            }
            for (const good of advancedGoods) {
                const rate = 60 - factory.level * 10
                let canReaction = true

                for (const substrate in COMMODITIES[good].components) {
                    const type = substrate as FactoryTarget

                    const require = COMMODITIES[good].components[type] * rate * 2
                    if (storage.store[type] < require) {
                        canReaction = false
                        if (!COMMODITIES[type].level) continue

                        const roomNames = FactoryController.getRoom(COMMODITIES[type].level)
                        for (const roomName of roomNames) {
                            if (roomName === room.name) continue

                            const targetRoom = Game.rooms[roomName]
                            if (!targetRoom || !isMyRoom(targetRoom) || targetRoom.controller.level < 6) continue
                            if (!targetRoom.terminal || !targetRoom.storage || targetRoom.storage.store[type] < require)
                                continue
                            if (ResourceManager.getSendTaskByType(targetRoom, type).length) continue

                            ResourceManager.addSendTask(targetRoom, room.name, type, require)
                        }

                        break
                    }
                }

                if (!canReaction) continue
                memory.target = good
                FactoryController.publishSubstrateTransportTask(factory, good, rate)
                return
            }
        }

        // bar
        const minerals: (MineralConstant | 'G')[] = ['H', 'O', 'U', 'L', 'K', 'Z', 'X', 'G']
        for (const mineral of minerals) {
            const mineralAmount = storage.store[mineral] || 0
            const barAmount = storage.store[zipMap[mineral]] || 0
            const rate = 10

            if (mineralAmount > 40000 || (mineralAmount > 10000 && barAmount < FactoryController.BAR_MIN_AMOUNT)) {
                memory.target = zipMap[mineral]
                FactoryController.publishSubstrateTransportTask(factory, zipMap[mineral], rate)
                return
            }

            if (
                barAmount > FactoryController.BAR_MIN_AMOUNT + 5000 &&
                mineralAmount + factory.store[mineral] + (room.terminal?.store[mineral] || 0) < 10000
            ) {
                memory.target = mineral
                FactoryController.publishSubstrateTransportTask(factory, mineral, rate)
                return
            }
        }

        // 基本区域性商品
        const baseGoods: CommodityConstant[] = ['wire', 'cell', 'alloy', 'condensate']
        for (const good of baseGoods) {
            const rate = 75
            if (
                Object.entries(COMMODITIES[good].components).find(
                    ([type, require]) => storage.store[type as FactoryTarget] < require * rate * 2,
                )
            ) {
                continue
            }
            memory.target = good
            FactoryController.publishSubstrateTransportTask(factory, good, rate)
            return
        }

        // 常规高等商品
        for (const [index, good] of resourceList.commodityBase.entries()) {
            if (factory.level !== index + 1) continue
            if (storage.store[good] > 5000) continue
            const rate = 75
            if (
                Object.entries(COMMODITIES[good].components).find(
                    ([type, require]) => storage.store[type as FactoryTarget] < require * rate * 2,
                )
            ) {
                continue
            }
            memory.target = good
            FactoryController.publishSubstrateTransportTask(factory, good, rate)
            return
        }
    }

    /**
     * 发布生产目标产物的底物的搬运任务
     */
    public static publishSubstrateTransportTask(factory: StructureFactory, target: FactoryTarget, rate = 10) {
        const resInfo: OneToOneTransportData['resInfo'] = {}

        Object.keys(COMMODITIES[target].components).forEach((type: FactoryTarget) => {
            const needAmount = COMMODITIES[target].components[type] * rate - factory.store[type]
            if (needAmount <= 0) return

            resInfo[type] = {
                require: needAmount,
                has: 0,
            }
        })

        if (Object.keys(resInfo).length === 0) return

        const data: OneToOneTransportData = {
            sourceId: factory.room.storage!.id,
            targetId: factory.id,
            resInfo,
        }
        TransportTaskManager.addOneToOneTransportTask(
            factory.room as MyRoom,
            FactoryController.FACTORY_TRANSPORT_SUBSTRATE_TASK_NAME,
            data,
        )
    }

    /**
     * 移出工厂中与生产无关的资源
     */
    public static clear(room: MyRoom, factory: StructureFactory, memory: FactoryMemory) {
        if (
            !factory.store.getUsedCapacity() ||
            TransportTaskManager.searchTransportTaskByName(room, FactoryController.FACTORY_CLEAR_PRODUCT_TASK_NAME)
                .length
        ) {
            return
        }

        let target: StructureStorage | StructureTerminal | undefined
        if (room.storage && room.storage.store.getFreeCapacity() > 30000) target = room.storage
        else if (room.terminal && room.terminal.store.getFreeCapacity() > 20000) target = room.terminal
        else return

        const substrate = (memory.target ? COMMODITIES[memory.target].components : {}) as Record<
            ResourceConstant,
            number
        >
        const resInfo: OneToOneTransportData['resInfo'] = {}
        let freeSpace = target.store.getFreeCapacity()
        Object.keys(factory.store).forEach((type: ResourceConstant) => {
            if (substrate[type]) return
            if (freeSpace <= factory.store[type]) return

            const amount = Math.min(factory.store[type], freeSpace)
            resInfo[type] = {
                require: amount,
                has: 0,
            }
            freeSpace -= amount
        })

        // 没有需要清理的资源
        if (freeSpace === target.store.getFreeCapacity()) return

        const data: OneToOneTransportData = {
            sourceId: factory.id,
            targetId: target.id,
            resInfo,
        }
        TransportTaskManager.addOneToOneTransportTask(room, FactoryController.FACTORY_CLEAR_PRODUCT_TASK_NAME, data)
    }

    /**
     * 添加房间到工厂对应表
     */
    public static addRoom(factory: StructureFactory) {
        const factoryRoomMap = FactoryController.factoryRoomMap
        const level = factory.level || 0
        if (!factoryRoomMap[level]) factoryRoomMap[level] = new Set()
        factoryRoomMap[level].add(factory.room.name)
    }

    /**
     * 获取指定等级的工厂所在房间
     */
    public static getRoom(level = 0): string[] {
        return [...(FactoryController.factoryRoomMap[level] || [])]
    }
}
